/*
 * Entrada.cpp
 *
 *  Created on: 02/10/2012
 */

#include "Entrada.h"

Entrada::Entrada(Estacionamiento* estacionamiento, int numeroEntrada, vector<Pipe>* pipesSalidas, vector<Semaforo>* semaforosSalidas):
	numeroEntrada (numeroEntrada),
	entradaAbierta (true),
	estacionamiento (estacionamiento),
	pipesSalidas (pipesSalidas),
	semaforosSalidas (semaforosSalidas){

	srand(getMilisegundos());
	SignalHandler::getInstance()->registrarHandler(SIGUSR1, this);
	SignalHandler::getInstance()->registrarHandler(SIGINT, this);
	SignalHandler::getInstance()->registrarHandler(SIGTERM, this);

	for (vector<Pipe>::iterator it = this->pipesSalidas->begin(); it != this->pipesSalidas->end(); it++){
		it->setearModo(ESCRITURA);
	}

	Log::getInstance()->loguear("Entrada " + intToString(numeroEntrada) + ": creada.");
}

Entrada::~Entrada() {
	SignalHandler::getInstance()->removerHandler(SIGUSR1);
	SignalHandler::getInstance()->removerHandler(SIGINT);
	SignalHandler::getInstance()->removerHandler(SIGTERM);
	SignalHandler::destruir();

	// Cierro los pipes
	for (vector<Pipe>::iterator it = pipesSalidas->begin(); it != pipesSalidas->end(); it++){
		it->cerrar();
	}

	Log::getInstance()->loguear("Entrada " + intToString(this->numeroEntrada) + ": destruida.");
}

/**
 * Handler de señales:
 * SIGUSR1: Señal que envia el terminador para finalizar la simulación.
 */
int Entrada::handleSignal(int signum){
	if (signum == SIGUSR1){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEntrada) + ": me llego SIGUSR1. Voy a finalizar.");
		this->entradaAbierta = false;

	} else if (signum == SIGINT){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEntrada) + ": me llego SIGINT. No hago nada.");
		// La señal la toma el terminador para detener la ejecución.

	} else if (signum == SIGTERM){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEntrada) + ": me llego SIGTERM. No hago nada.");
		// La señal la toma el terminador para detener la ejecución.

	}
	return 0;
}

/**
 * Duerme el proceso simulando la espera del arribo de un auto.
 */
void Entrada::esperarAuto(){

	// Se calculan varios numeros random para que sea mas aleatorio y haya diferencia entre las entradas.
	int periodoMinimo = calcularRandom(PERIODO_LLEGADA_AUTOS_MINIMO, PERIODO_LLEGADA_AUTOS_MAXIMO / 2);
	int periodoMaximo = calcularRandom(PERIODO_LLEGADA_AUTOS_MAXIMO / 2, PERIODO_LLEGADA_AUTOS_MAXIMO);

	// Se usa usleep y se multiplica por el numero de la entrada con el mismo objetivo de que haya mas aleatoriedad entre entradas.
	usleep(this->numeroEntrada * 100000 + calcularRandom(periodoMinimo, periodoMaximo) * 1000000);
}

Auto Entrada::generarAuto(){
	Log::getInstance()->loguear("Entrada " + intToString(this->numeroEntrada) + ": se genera un auto.");

	int horasEstacionado = calcularRandom(HORAS_ESTACIONADO_MINIMAS, HORAS_ESTACIONADO_MAXIMAS);
	Auto _auto(horasEstacionado, this->pipesSalidas, this->semaforosSalidas);
	_auto.inicializar();
	return _auto;
}

/**
 * Simula la llegada de un auto al estacionamiento con una frecuencia aleatoria.
 */
int Entrada::recibirAutos(){

	this->esperarAuto();

	while(this->entradaAbierta){
		pid_t pid = fork();

		if (pid == 0){

			Auto _auto = this->generarAuto();
			Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEntrada) + 
					": Llega un auto que va a estacionar " + intToString(_auto.getHorasEstacionado()) +
					" horas.");

			int lugarDisponible = estacionamiento->buscarReservarLugarDisponible();

			if (lugarDisponible != -1){
				Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEntrada) + 
						": El auto estaciona en el lugar " + intToString(lugarDisponible) + ".");

				this->estacionamiento->aumentarAutos();

				_auto.setTicket(Ticket(lugarDisponible, _auto.getHorasEstacionado()));
				_auto.estacionar();

			} else {
				Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEntrada) +
						": No hay lugares disponibles.");
			}

			// Si no hay lugar disponible el proceso del auto se muere.
			return 1;
		}
		this->esperarAuto();
	}
	Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEntrada)
			+ ": No se aceptan más autos.");
	return 0;
}


